/*-------------------------------------------------------------------------
 *
 * pqcomm.h
 *		前端和后端通用的定义。
 *
 * 注意：出于历史原因，这与pqcomm.c不对应。
 * pqcomm.c的例程在libpq.h中声明。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/libpq/pqcomm.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef PQCOMM_H
#define PQCOMM_H

#include <sys/socket.h>
#include <netdb.h>
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#include <netinet/in.h>

#ifdef HAVE_STRUCT_SOCKADDR_STORAGE

#ifndef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY
#define ss_family __ss_family
#else
#error struct sockaddr_storage does not provide an ss_family member
#endif
#endif

#ifdef HAVE_STRUCT_SOCKADDR_STORAGE___SS_LEN
#define ss_len __ss_len
#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN 1
#endif
#else							/* !HAVE_STRUCT_SOCKADDR_STORAGE */

/* 如果我们没有定义struct sockaddr_storage，则定义一个。 */

struct sockaddr_storage
{
	union
	{
		struct sockaddr sa;		/* 获取系统相关字段 */
		int64		ss_align;	/* 确保结构体正确对齐 */
		char		ss_pad[128];	/* 确保结构体具有所需的大小 */
	}			ss_stuff;
};

#define ss_family	ss_stuff.sa.sa_family
/* 如果sockaddr具有sa_len，则应该有一个ss_len字段。 */
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
#define ss_len		ss_stuff.sa.sa_len
#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN 1
#endif
#endif							/* HAVE_STRUCT_SOCKADDR_STORAGE */

typedef struct
{
	struct sockaddr_storage addr;
	socklen_t	salen;
} SockAddr;

/* 配置UNIX套接字在知名端口的位置。 */

#define UNIXSOCK_PATH(path, port, sockdir) \
	   (AssertMacro(sockdir), \
		AssertMacro(*(sockdir) != '\0'), \
		snprintf(path, sizeof(path), "%s/.s.PGSQL.%d", \
				 (sockdir), (port)))

/*
 * 套接字路径的最大可用长度是可以适配到
 * struct sockaddr_un中的长度。这通常只有100个字节左右 :-(。
 *
 * 为了一致性，总是将一个MAXPGPATH大小的缓冲区传递给UNIXSOCK_PATH（），
 * 如果结果字符串的长度大于或等于UNIXSOCK_PATH_BUFLEN字节则抱怨。
 * （因为getaddrinfo的标准API不支持在
 * 套接字路径名过长时以有用的方式抱怨，所以我们必须显式测试
 * 这一点，而不是让子例程直接返回错误。）
 */
#define UNIXSOCK_PATH_BUFLEN sizeof(((struct sockaddr_un *) NULL)->sun_path)

/*
 * 看起来像绝对路径或者以@开头的主机被
 * 解释为Unix域套接字地址。
 */
static inline bool
is_unixsock_path(const char *path)
{
	return is_absolute_path(path) || path[0] == '@';
}

/*
 * 这些操作用于管理前端/后端协议版本号。
 *
 * 主要版本号应该在不兼容变更时递增。次要
 * 版本号应该在兼容变更时递增（例如，新增
 * 功能）。
 *
 * 如果后端支持协议的版本m.n，它实际上必须支持
 * 版本m.[0..n]。在“合理”的时间段后，可以放弃
 * 对版本m-1的后端支持。
 *
 * 前端不需要支持除当前
 * 版本之外的其他版本。
 */

#define PG_PROTOCOL_MAJOR(v)	((v) >> 16)
#define PG_PROTOCOL_MINOR(v)	((v) & 0x0000ffff)
#define PG_PROTOCOL(m,n)	(((m) << 16) | (n))

/*
 * 支持的最早和最新的前端/后端协议版本。
 * （目前只支持协议版本3）
 */

#define PG_PROTOCOL_EARLIEST	PG_PROTOCOL(3,0)
#define PG_PROTOCOL_LATEST		PG_PROTOCOL(3,0)

typedef uint32 ProtocolVersion; /* FE/BE协议版本号 */

typedef ProtocolVersion MsgType;


/*
 * 数据包长度为网络字节序的4个字节。
 *
 * 初始长度在下面出现的数据包布局中被省略。
 */

typedef uint32 PacketLen;

extern PGDLLIMPORT bool Db_user_namespace;

/*
 * 在协议3.0及以后的版本中，启动数据包长度是不固定的，但
 * 我们仍然设定一个任意限制。这只是为了防止通过发送足够的数据
 * 来使服务器耗尽内存的简单拒绝服务攻击。
 */
#define MAX_STARTUP_PACKET_LENGTH 10000


/* 这些是后端发送的认证请求代码。 */

#define AUTH_REQ_OK			0	/* 用户已通过认证 */
#define AUTH_REQ_KRB4		1	/* Kerberos V4。不再支持。 */
#define AUTH_REQ_KRB5		2	/* Kerberos V5。不再支持。 */
#define AUTH_REQ_PASSWORD	3	/* Password */
#define AUTH_REQ_CRYPT		4	/* crypt密码。不再支持。 */
#define AUTH_REQ_MD5		5	/* md5密码 */
#define AUTH_REQ_SCM_CREDS	6	/* 传输SCM凭证 */
#define AUTH_REQ_GSS		7	/* 无wrap()的GSSAPI */
#define AUTH_REQ_GSS_CONT	8	/* 继续GSS交换 */
#define AUTH_REQ_SSPI		9	/* 无wrap()的SSPI协商 */
#define AUTH_REQ_SASL	   10	/* 开始SASL认证 */
#define AUTH_REQ_SASL_CONT 11	/* 继续SASL认证 */
#define AUTH_REQ_SASL_FIN  12	/* 最终的SASL消息 */

#ifdef FDDGM
#define AUTH_REQ_SM3 13        /* sm3密码 */
#endif

typedef uint32 AuthRequest;


/*
 * 客户端也可以向主控进程发送取消当前操作的请求。
 * 这比直接发送到客户端的后端更复杂，但它
 * 避免了对带外通信设施的依赖。
 *
 * 取消请求代码不得与我们可能使用的任何协议版本号
 * 匹配。这个随机选择应该可以做到。
 */
#define CANCEL_REQUEST_CODE PG_PROTOCOL(1234,5678)

typedef struct CancelRequestPacket
{
	/* 注意每个字段以网络字节序存储！ */
	MsgType		cancelRequestCode;	/* 识别取消请求的代码 */
	uint32		backendPID;		/* 客户端后端的PID */
	uint32		cancelAuthCode; /* 授权取消的秘密密钥 */
} CancelRequestPacket;


/*
 * 客户端也可以通过发送SSL或GSSAPI协商请求开始
 * 获取安全通道。
 */
#define NEGOTIATE_SSL_CODE PG_PROTOCOL(1234,5679)
#define NEGOTIATE_GSS_CODE PG_PROTOCOL(1234,5680)

#endif							/* PQCOMM_H */
